#include <linux/vmalloc.h>
#include "utils/ringbuffer.h"


/**
 * \brief  Removes the entire FIFO contents.
 * \param  [in] fifo: The fifo to be emptied.
 * \return N/A.
 */
void ringbuffer_reset(ringbuffer_t* fifo)
{
    fifo->write = fifo->read = 0;
    fifo->data_len           = 0;
}

/**
 * \brief  Returns the size of the FIFO in bytes.
 * \param  [in] fifo: The fifo to be used.
 * \return The size of the FIFO.
 */
static inline u32 ringbuffer_size(ringbuffer_t* fifo)
{
    return fifo->size;
}

/**
 * \brief  Returns the number of used bytes in the FIFO.
 * \param  [in] fifo: The fifo to be used.
 * \return The number of used bytes.
 */
u32 ringbuffer_len(ringbuffer_t* fifo)
{
    return fifo->data_len;
}

/**
 * \brief  Returns the number of bytes available in the FIFO.
 * \param  [in] fifo: The fifo to be used.
 * \return The number of bytes available.
 */
u32 ringbuffer_avail(ringbuffer_t* fifo)
{
    return ringbuffer_size(fifo) - ringbuffer_len(fifo);
}

/**
 * \brief  Is the FIFO empty?
 * \param  [in] fifo: The fifo to be used.
 * \retval true:      Yes.
 * \retval false:     No.
 */
bool ringbuffer_is_empty(ringbuffer_t* fifo)
{
    return ringbuffer_len(fifo) == 0;
}

/**
 * \brief  Is the FIFO full?
 * \param  [in] fifo: The fifo to be used.
 * \retval true:      Yes.
 * \retval false:     No.
 */
bool ringbuffer_is_full(ringbuffer_t* fifo)
{
    return ringbuffer_avail(fifo) == 0;
}

/**
 * \brief  Puts some data into the FIFO.
 * \param  [in] fifo: The fifo to be used.
 * \param  [in] in:   The data to be added.
 * \param  [in] len:  The length of the data to be added.
 * \return The number of bytes copied.
 * \note   This function copies at most @len bytes from the @in into
 *         the FIFO depending on the free space, and returns the number
 *         of bytes copied.
 */
u32 ringbuffer_in(ringbuffer_t* fifo, const void* datptr, u32 len)
{
    u32 writelen = 0, tmplen = 0;

    if (ringbuffer_is_full(fifo))
        return 0;

    tmplen   = fifo->size - fifo->data_len;
    writelen = tmplen > len ? len : tmplen;

    if (fifo->write < fifo->read) {
        memcpy((void*)&fifo->buffer[fifo->write], (void*)datptr, writelen);
    } else {
        tmplen = fifo->size - fifo->write;
        if (writelen <= tmplen) {
            memcpy((void*)&fifo->buffer[fifo->write], (void*)datptr, writelen);
        } else {
            memcpy((void*)&fifo->buffer[fifo->write], (void*)datptr, tmplen);
            memcpy((void*)fifo->buffer, (u8*)datptr + tmplen, writelen - tmplen);
        }
    }

    fifo->write = (fifo->write + writelen) % fifo->size;
    fifo->data_len += writelen;

    return writelen;
}

/**
 * \brief  Gets some data from the FIFO.
 * \param  [in] fifo: The fifo to be used.
 * \param  [in] out:  Where the data must be copied.
 * \param  [in] len:  The size of the destination buffer.
 * \return The number of copied bytes.
 * \note   This function copies at most @len bytes from the FIFO into
 *         the @out and returns the number of copied bytes.
 */
u32 ringbuffer_out(ringbuffer_t* fifo, void* outbuf, u32 len)
{
    u32 readlen = 0, tmplen = 0;
    if (ringbuffer_is_empty(fifo))
        return 0;

    readlen = len > fifo->data_len ? fifo->data_len : len;
    tmplen  = fifo->size - fifo->read;

    if (NULL != outbuf) {
        if (readlen <= tmplen) {
            memcpy((void*)outbuf, (void*)&fifo->buffer[fifo->read], readlen);
        } else {
            memcpy((void*)outbuf, (void*)&fifo->buffer[fifo->read], tmplen);
            memcpy((u8*)outbuf + tmplen, (void*)fifo->buffer, readlen - tmplen);
        }
    }

    fifo->read = (fifo->read + readlen) % fifo->size;
    fifo->data_len -= readlen;

    return readlen;
}

/**
  * \brief  Gets some data from the FIFO without remove them in FIFO.
  * \param  [in] fifo:    The fifo to be used.
  * \param  [in] pos:     Where the data start to be copied.
  * \param  [in] outbuf:  Where the data must be copied.
  * \param  [in] len:     The size of the destination buffer.
  * \return The number of copied bytes.
  * \note   This function copies at most @len bytes from the FIFO into
  *         the @out and returns the number of copied bytes.
  */
u32 ringbuffer_pick(ringbuffer_t *fifo, u32 pos, void *outbuf, u32 len)
{
    u32 readlen = 0, tmplen = 0;
    if(ringbuffer_is_empty(fifo))
        return 0;

    if (pos >= fifo->size) {
        return 0;
    }

    readlen = len > fifo->data_len ? fifo->data_len : len;
    tmplen = fifo->size - pos;

    if(NULL != outbuf) {
        if(readlen <= tmplen) {
            memcpy((void*)outbuf, (void*)&fifo->buffer[pos], readlen);
        } else {
            memcpy((void*)outbuf,(void*)&fifo->buffer[pos], tmplen);
            memcpy((u8*)outbuf + tmplen,(void*)fifo->buffer,readlen - tmplen);
        }
    }

    return readlen;
}
